/*
 * This file is part of the dSploit.
 *
 * Copyleft of Simone Margaritelli aka evilsocket <evilsocket@gmail.com>
 *
 * dSploit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * dSploit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with dSploit.  If not, see <http://www.gnu.org/licenses/>.
 */
package it.evilsocket.dsploit.plugins;

import java.util.ArrayList;
import java.util.HashMap;

import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.text.Html;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.ToggleButton;
import it.evilsocket.dsploit.R;
import it.evilsocket.dsploit.core.Plugin;
import it.evilsocket.dsploit.core.System;
import it.evilsocket.dsploit.gui.dialogs.FinishDialog;
import it.evilsocket.dsploit.net.Target;
import it.evilsocket.dsploit.net.Target.Port;
import it.evilsocket.dsploit.net.Target.Vulnerability;

public class ExploitFinder extends Plugin 
{
	private final static String  TAG = "EXPLOITFINDER";

	private ToggleButton       mSearchToggleButton = null;
	private ProgressBar	       mSearchProgress     = null;
	private ExpandableListView mListView		   = null;		
	private ListViewAdapter	   mAdapter			   = null;
	private boolean	           mRunning			   = false;
	private Thread			   mThread			   = null;
		
	public class ListViewAdapter extends BaseExpandableListAdapter
	{
		private HashMap< String, ArrayList<Vulnerability> > mGroups  = null;
		private Context							 		    mContext = null;

		public ListViewAdapter( Context context ) {
			mGroups  = new HashMap< String, ArrayList<Vulnerability> >();
			mContext = context; 
		}
		
		public void clear(){
			Object[] keys = mGroups.keySet().toArray();
			
			for( Object key : keys )
			{
				mGroups.get(key).clear();
			}
			
			notifyDataSetChanged();
		}
		
		public boolean hasGroup( String name ) {
			return mGroups.containsKey(name);
		}
		
		public void addGroup( String name ) {
			mGroups.put( name, new ArrayList<Vulnerability>() );
			notifyDataSetChanged();
		}
		
		public void addChild( String group, Vulnerability child ) {			
			if( !hasGroup( group ) )
				addGroup( group );
			
			mGroups.get( group ).add( child );
			
			notifyDataSetChanged();
		}
		
		private ArrayList<Vulnerability> getGroupAt( int position ){
			return mGroups.get( mGroups.keySet().toArray()[ position ] );
		}
		
		@Override
		public Object getChild( int groupPosition, int childPosition ) {
			return getGroupAt( groupPosition ).get( childPosition );
		}

		@Override
		public long getChildId( int groupPosition, int childPosition ) {			
			return ( groupPosition * 10 ) + childPosition;
		}

		@Override
		public int getChildrenCount( int groupPosition ) {
			return getGroupAt( groupPosition ).size();
		}

		@Override
		public Object getGroup( int groupPosition ) {
			return mGroups.keySet().toArray()[ groupPosition ];
		}

		@Override
		public int getGroupCount() {
			return mGroups.size();
		}

		@Override
		public long getGroupId(int groupPosition) {
			return groupPosition;
		}

		@Override
		public View getGroupView( int groupPosition, boolean isExpanded, View convertView, ViewGroup parent ) {
			TextView row = (TextView)convertView;
		    if( row == null ) 		    
		      row = new TextView( mContext);
		    
		    row.setText( getGroup( groupPosition ).toString() );
		    row.setTextSize( 15 );
		    row.setTypeface( Typeface.DEFAULT_BOLD );
		    row.setPadding( 50, 0, 0, 0 );
		    
		    return row;
		}
		
		@Override
		public View getChildView( int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent ) {
			TextView row = (TextView)convertView;
		    if( row == null ) 		    
		      row = new TextView( mContext );
		    
		    Vulnerability cve = (Vulnerability)getChild( groupPosition, childPosition );
		    
		    if( cve == null )
		    	row.setText( Html.fromHtml( "<small><i>Nothing Found</i></small>" ) );
		    else		    	
		    	row.setText
		    	(  
		    	  Html.fromHtml
		    	  (
		    		"<font color=\"" + cve.getHtmlColor() + "\"><b>" + cve.getIdentifier() + "</b></font> : <small>" + cve.getSummary() + "</small>"	  
		          )
		    	);
		    
		    row.setPadding( 30, 0, 0, 0 );
		    
		    return row;
		}

		@Override
		public boolean hasStableIds() {
			return true;
		}

		@Override
		public boolean isChildSelectable(int groupPosition, int childPosition) {
			return true;
		}		
	}
	
	public ExploitFinder() {
		super
		( 
		    "Vulnerability Finder", 
		    "Search for known vulnerabilities for target running services upon National Vulnerability Database.", 
		    new Target.Type[]{ Target.Type.ENDPOINT, Target.Type.REMOTE }, 
		    R.layout.plugin_exploit_finder,
		    R.drawable.action_exploit 
		);
	}
		
	private void setStartedState( ) {
		mSearchProgress.setVisibility( View.VISIBLE );
		mRunning = true;
		
		mAdapter.clear();
		
		mThread = new Thread( new Runnable()
		{ 
			@Override
			public void run() 
			{				
				for( final Port port : System.getCurrentTarget().getOpenPorts() )
		        {
					if( !mRunning ) return;
					
		        	if( mRunning && port.service != null && port.service.isEmpty() == false )
		        	{
		        		// remove version numbers
		        		String query = port.getServiceQuery();
		        				        		
		        		Log.d( TAG, "Searching for " + port.service + " ( QUERY = " + query + " )" );
		        		
		        		ArrayList<Vulnerability> results = NVDatabase.search( query );
		        		
		        		if( !mRunning ) return;
		        		
		        		if( results == null )
		        		{
		        			// error parsing the page
		        			Log.e( TAG, "Error parsing the page." );
		        		}
		        		else if( results.size() > 0 )
		        		{
		        			for( final Vulnerability cve : results )
		        			{
		        				System.addVulnerability( port, cve );
		        				
		        				ExploitFinder.this.runOnUiThread( new Runnable() {
									@Override
									public void run()
									{						
										mAdapter.addChild( port.service, cve );
									}							
								});			
		        			}
		        		}
		        		else
		        		{
		        			ExploitFinder.this.runOnUiThread( new Runnable() {
								@Override
								public void run()
								{						
									mAdapter.addChild( port.service, null );
								}							
							});		
		        		}
		        	}
		        }
				
				ExploitFinder.this.runOnUiThread( new Runnable() {
					@Override
					public void run()
					{						
						setStoppedState();
					}							
				});	
			}
		}  
		);
		
		mThread.start();
	}
	
	private void setStoppedState( ) {		
		try
		{
			if( mThread != null )
			{
				mThread.interrupt();
				mThread.stop();
			}
		}
		catch( Exception e )
		{
			
		}
		
		mRunning = false;
		mSearchToggleButton.setChecked( false );    
		mSearchProgress.setVisibility( View.GONE );
	}
	
	public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);   
        
        if( System.getCurrentTarget().hasOpenPorts() == false )
        	new FinishDialog( "Warning", "No open ports detected on current target, run the service inspector first.", this ).show();
        
        else if( System.getCurrentTarget().hasOpenPortsWithService() == false )
        	new FinishDialog( "Warning", "No specific informations about services running on target machine, run the service inspector first.", this ).show();
                
        mSearchToggleButton = ( ToggleButton )findViewById( R.id.searchToggleButton );
        mSearchProgress	   = ( ProgressBar )findViewById( R.id.searchActivity );
        mListView		   = ( ExpandableListView )findViewById( R.id.searchListView );
        mAdapter		   = new ListViewAdapter( this );
        
        HashMap< String, ArrayList< Vulnerability > > vulnerabilities = System.getCurrentTarget().getVulnerabilities();
        
        for( Port port : System.getCurrentTarget().getOpenPorts() )
        {
        	if( port.service != null && port.service.isEmpty() == false )
        	{
        		mAdapter.addGroup( port.service );
        		
        		if( vulnerabilities.containsKey( port.toString() ) )
        		{
	        		for( Vulnerability v : vulnerabilities.get( port.toString() ) )
	        		{
	        			mAdapter.addChild( port.service, v );
	        		}
        		}
        	}
        }
        		        
        mListView.setAdapter( mAdapter );                
        mListView.setOnChildClickListener( new OnChildClickListener(){
			@Override
			public boolean onChildClick( ExpandableListView parent, View v, int groupPosition, int childPosition, long id ) {
				Vulnerability cve = ( Vulnerability )mAdapter.getChild(groupPosition, childPosition);
				
				if( cve != null )
				{
					String uri     = "http://web.nvd.nist.gov/view/vuln/detail?vulnId=" + cve.getIdentifier();
					Intent browser = new Intent( Intent.ACTION_VIEW, Uri.parse( uri ) );
					
					startActivity( browser );
				}
				
				return true;
			}} 
        );
        
		for( int i = 0; i < mAdapter.getGroupCount(); i++ )
		{
			mListView.expandGroup( i );
		}
                
        mSearchToggleButton.setOnClickListener( new OnClickListener(){
			@Override
			public void onClick(View v) {
				if( mRunning )
				{
					setStoppedState();
				}
				else
				{
					setStartedState();
				}
			}} 
		);                
	}
	
	@Override
	public void onBackPressed() {
	    setStoppedState();	
	    super.onBackPressed();
	    overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_left);	    	    
	}
}
